package com.hbwxz.user.service;

import com.alibaba.fastjson.JSONObject;
import com.hbwxz.common.response.CommonResponse;
import com.hbwxz.common.response.ResponseCode;
import com.hbwxz.common.response.ResponseUtils;
import com.hbwxz.user.config.GiteeConfig;
import com.hbwxz.user.pojo.*;
import com.hbwxz.user.processor.RedisCommonProcessor;
import com.hbwxz.user.repo.OauthClientRegisterRepository;
import com.hbwxz.user.repo.OauthClientRepository;
import com.hbwxz.user.repo.UserRepository;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Service
public class UserRegisterLoginService {
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private OauthClientRepository oauthClientRepository;

    @Autowired
    private RedisCommonProcessor redisCommonProcessor;

    @Autowired
    private RestTemplate innerRestTemplate;

    @Autowired
    private RestTemplate outerRestTemplate;

    @Autowired
    private GiteeConfig giteeConfig;

    @Autowired
    private OauthClientRegisterRepository oauthClientRegisterRepository;

    @Resource(name = "transactionManager")
    private JpaTransactionManager transactionManager;
//    @Transactional(propagation = Propagation.REQUIRED)
    public CommonResponse namePasswordRegister(User user) {
        if(userRepository.findByUserName(user.getUserName()) == null || oauthClientRepository.findByClientId(user.getUserName())==null) {
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            String passwd = user.getPasswd();
            String encodePassword = passwordEncoder.encode(passwd);
            user.setPasswd(encodePassword);

            Oauth2Client oauth2Client = Oauth2Client.builder()
                    .clientId(user.getUserName())
                    .clientSecret(encodePassword)
                    .resourceIds(RegisterType.USER_PASSWORD.name())
                    .authorizedGrantTypes(AuthGrantType.refresh_token.name().concat(",").concat(AuthGrantType.password.name()))
                    .scope("web")
                    .authorities(RegisterType.USER_PASSWORD.name())
                    .build();

            Integer uid = this.saveUserAndOauthClient(user, oauth2Client);
            String personId = uid + 10000000 +"";
            redisCommonProcessor.setExpiredDays(personId,user,30);

            return ResponseUtils.okResponse(formatResponseContent(user,generateOauthToken(AuthGrantType.password,user.getUserName(),passwd,user.getUserName(),passwd)));

        }
        return ResponseUtils.failResponse(ResponseCode.BAD_REQUEST.getCode(),null,"User already exists");
    }

    public CommonResponse phoneCodeRegister(String phoneNumber, String code) {
        String cacheCode = String.valueOf(redisCommonProcessor.get(phoneNumber));
        if(StringUtils.isEmpty(cacheCode)){
            return ResponseUtils.failResponse(ResponseCode.BAD_REQUEST.getCode(), null,"Phone is expired");
        }
        if(!code.equals(cacheCode)){
            return ResponseUtils.failResponse(ResponseCode.BAD_REQUEST.getCode(), null,"Phone is wrong");
        }
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encodePassword = passwordEncoder.encode(code);
        User user = userRepository.findByUserPhone(phoneNumber);
        if(user==null){
            String username = getSystemDefineUserName(phoneNumber);
            user = User.builder()
                    .userName(username)
                    .passwd("")
                    .userPhone(phoneNumber)
                    .userRole(RegisterType.PHONE_NUMBER.name())
                    .build();

            Oauth2Client oauth2Client = Oauth2Client.builder()
                    .clientId(phoneNumber)
                    .clientSecret(encodePassword)
                    .resourceIds(RegisterType.PHONE_NUMBER.name())
                    .authorizedGrantTypes(AuthGrantType.refresh_token.name().concat(",").concat(AuthGrantType.client_credentials.name()))
                    .scope("web")
                    .authorities(RegisterType.PHONE_NUMBER.name())
                    .build();
            Integer userId = this.saveUserAndOauthClient(user, oauth2Client);
            String personId = userId + 10000000 +"";
            redisCommonProcessor.setExpiredDays(personId,user,30);
//            return ResponseUtils.okResponse(formatResponseContent(user,generateOauthToken(AuthGrantType.password,user.getUserName(),passwd,user.getUserName(),passwd)));
        }else{//说明cache过期了
            oauthClientRepository.updateSecretByClientId(encodePassword,phoneNumber);
        }

        return ResponseUtils.okResponse(formatResponseContent(user,
                generateOauthToken(AuthGrantType.client_credentials,null,null,phoneNumber,code)));
    }

    public CommonResponse thirdPartGiteeCallback(HttpServletRequest request) {
        String code = request.getParameter("code");
        String state = request.getParameter("state");
        if(!giteeConfig.getState().equals(state)){
            throw new UnsupportedOperationException("Invalid state!");
        }

        String tokenUrl = String.format(giteeConfig.getTokenUrl(), giteeConfig.getClientId(), giteeConfig.getClientSecret(), giteeConfig.getCallBack(), code);
        JSONObject tokenResult = outerRestTemplate.postForObject(tokenUrl, null, JSONObject.class);
        String token = String.valueOf(tokenResult.get("access_token"));

        String userUrl = String.format(giteeConfig.getUserUrl(),token);
        JSONObject userInfo = outerRestTemplate.getForObject(userUrl, JSONObject.class);

        String username = giteeConfig.getState().concat("_").concat(String.valueOf(userInfo.get("login")));
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String encodePassword = bCryptPasswordEncoder.encode(username);

        User user = userRepository.findByUserName(username);
        if(user==null){
            user = User.builder()
                    .userName(username)
                    .passwd("")
                    .userRole(RegisterType.THIRD_PARTY.name())
                    .build();

            Oauth2Client oauth2Client = Oauth2Client.builder()
                    .clientId(username)
                    .clientSecret(encodePassword)
                    .resourceIds(RegisterType.THIRD_PARTY.name())
                    .authorizedGrantTypes(AuthGrantType.refresh_token.name().concat(",").concat(AuthGrantType.client_credentials.name()))
                    .scope("web")
                    .authorities(RegisterType.THIRD_PARTY.name())
                    .build();
            Integer userId = this.saveUserAndOauthClient(user, oauth2Client);
            String personId = userId + 10000000 +"";
            redisCommonProcessor.setExpiredDays(personId,user,30);
        }
        return ResponseUtils.okResponse(formatResponseContent(user,
                generateOauthToken(AuthGrantType.client_credentials,null,null,username,username)));
    }

    private String getSystemDefineUserName(String phoneNumber) {
        return "MALL"+System.currentTimeMillis()+phoneNumber.substring(phoneNumber.length()-4);
    }

    private Map formatResponseContent(User user,Map oauth2Client) {
        return new HashMap(){{
            put("user", user);
            put("oauth", oauth2Client);
        }};
    }

    private Integer saveUserAndOauthClient(User user, Oauth2Client oauth2Client) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        def.setTimeout(30);
        TransactionStatus status = transactionManager.getTransaction(def);
        try{
            user = this.userRepository.save(user);
            this.oauthClientRepository.save(oauth2Client);
            transactionManager.commit(status);
        }catch (Exception e) {
            if(!status.isCompleted()) {
                transactionManager.rollback(status);
            }
            throw new UnsupportedOperationException("DB save failed");
        }
        return user.getId();
    }

    private Map generateOauthToken(AuthGrantType authGrantType, String username, String password, String clientId, String clientSecret){
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//        headers.setBasicAuth(clientId, clientSecret); // ⚠️关键缺失点

        MultiValueMap<String,String> params = new LinkedMultiValueMap<>();
        params.add("grant_type", authGrantType.name());
        params.add("client_id", clientId);
        params.add("client_secret", clientSecret);
        if(authGrantType == AuthGrantType.password){
            params.add("username", username);
            params.add("password", password);
        }
        HttpEntity<MultiValueMap<String,String>> requestEntity =
                new HttpEntity<>(params, headers);
        return innerRestTemplate.postForObject("http://oauth2-service/oauth/token",requestEntity,Map.class);
    }


    public CommonResponse login(String username, String password) {
        User user = userRepository.findByUserName(username);
        if(user == null){
            return ResponseUtils.failResponse(ResponseCode.BAD_REQUEST.getCode(), null,"user not exist!");
        }

        Map content = formatResponseContent(user,
                generateOauthToken(AuthGrantType.password,username,password,username,password));
        String personId = String.valueOf(user.getId()+10000000+"");
        redisCommonProcessor.setExpiredDays(personId,user,30);
        return ResponseUtils.okResponse(content);
    }

    public CommonResponse thirdPartAppRequest(String personId, Oauth2ClientRegister oauth2ClientRegister) {
        Integer userId = Integer.valueOf(personId)-10000000;
        oauth2ClientRegister.setClientId(UUID.randomUUID().toString().replaceAll("-",""));
        oauth2ClientRegister.setClientSecret(UUID.randomUUID().toString().replaceAll("-",""));
        oauth2ClientRegister.setApprove(0);
        oauth2ClientRegister.setUserId(userId);
        oauthClientRegisterRepository.save(oauth2ClientRegister);
        return ResponseUtils.okResponse(null);
    }

    public CommonResponse checkThirdPartAppRequestStatus(String personId) {
        Integer userId = Integer.valueOf(personId)-10000000;
        List<Oauth2ClientRegister> userRequestInfo = oauthClientRegisterRepository.findByUserId(userId);
        return ResponseUtils.okResponse(userRequestInfo);
    }
}
